<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="generator" content="AsciiDoc 8.4.5" />
<title>store-it Documentation</title>
<style type="text/css">
/* Debug borders */
p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
/*
  border: 1px solid red;
*/
}

body {
  margin: 1em 5% 1em 5%;
}

a {
  color: blue;
  text-decoration: underline;
}
a:visited {
  color: fuchsia;
}

em {
  font-style: italic;
  color: navy;
}

strong {
  font-weight: bold;
  color: #083194;
}

tt {
  color: navy;
}

h1, h2, h3, h4, h5, h6 {
  color: #527bbd;
  font-family: sans-serif;
  margin-top: 1.2em;
  margin-bottom: 0.5em;
  line-height: 1.3;
}

h1, h2, h3 {
  border-bottom: 2px solid silver;
}
h2 {
  padding-top: 0.5em;
}
h3 {
  float: left;
}
h3 + * {
  clear: left;
}

div.sectionbody {
  font-family: serif;
  margin-left: 0;
}

hr {
  border: 1px solid silver;
}

p {
  margin-top: 0.5em;
  margin-bottom: 0.5em;
}

ul, ol, li > p {
  margin-top: 0;
}

pre {
  padding: 0;
  margin: 0;
}

span#author {
  color: #527bbd;
  font-family: sans-serif;
  font-weight: bold;
  font-size: 1.1em;
}
span#email {
}
span#revnumber, span#revdate, span#revremark {
  font-family: sans-serif;
}

div#footer {
  font-family: sans-serif;
  font-size: small;
  border-top: 2px solid silver;
  padding-top: 0.5em;
  margin-top: 4.0em;
}
div#footer-text {
  float: left;
  padding-bottom: 0.5em;
}
div#footer-badges {
  float: right;
  padding-bottom: 0.5em;
}

div#preamble {
  margin-top: 1.5em;
  margin-bottom: 1.5em;
}
div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
  margin-top: 1.5em;
  margin-bottom: 1.5em;
}
div.admonitionblock {
  margin-top: 2.5em;
  margin-bottom: 2.5em;
}

div.content { /* Block element content. */
  padding: 0;
}

/* Block element titles. */
div.title, caption.title {
  color: #527bbd;
  font-family: sans-serif;
  font-weight: bold;
  text-align: left;
  margin-top: 1.0em;
  margin-bottom: 0.5em;
}
div.title + * {
  margin-top: 0;
}

td div.title:first-child {
  margin-top: 0.0em;
}
div.content div.title:first-child {
  margin-top: 0.0em;
}
div.content + div.title {
  margin-top: 0.0em;
}

div.sidebarblock > div.content {
  background: #ffffee;
  border: 1px solid silver;
  padding: 0.5em;
}

div.listingblock > div.content {
  border: 1px solid silver;
  background: #f4f4f4;
  padding: 0.5em;
}

div.quoteblock {
  padding-left: 2.0em;
  margin-right: 10%;
}
div.quoteblock > div.attribution {
  padding-top: 0.5em;
  text-align: right;
}

div.verseblock {
  padding-left: 2.0em;
  margin-right: 10%;
}
div.verseblock > div.content {
  white-space: pre;
}
div.verseblock > div.attribution {
  padding-top: 0.75em;
  text-align: left;
}
/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
div.verseblock + div.attribution {
  text-align: left;
}

div.admonitionblock .icon {
  vertical-align: top;
  font-size: 1.1em;
  font-weight: bold;
  text-decoration: underline;
  color: #527bbd;
  padding-right: 0.5em;
}
div.admonitionblock td.content {
  padding-left: 0.5em;
  border-left: 2px solid silver;
}

div.exampleblock > div.content {
  border-left: 2px solid silver;
  padding: 0.5em;
}

div.imageblock div.content { padding-left: 0; }
span.image img { border-style: none; }
a.image:visited { color: white; }

dl {
  margin-top: 0.8em;
  margin-bottom: 0.8em;
}
dt {
  margin-top: 0.5em;
  margin-bottom: 0;
  font-style: normal;
  color: navy;
}
dd > *:first-child {
  margin-top: 0.1em;
}

ul, ol {
    list-style-position: outside;
}
ol.arabic {
  list-style-type: decimal;
}
ol.loweralpha {
  list-style-type: lower-alpha;
}
ol.upperalpha {
  list-style-type: upper-alpha;
}
ol.lowerroman {
  list-style-type: lower-roman;
}
ol.upperroman {
  list-style-type: upper-roman;
}

div.compact ul, div.compact ol,
div.compact p, div.compact p,
div.compact div, div.compact div {
  margin-top: 0.1em;
  margin-bottom: 0.1em;
}

div.tableblock > table {
  border: 3px solid #527bbd;
}
thead {
  font-family: sans-serif;
  font-weight: bold;
}
tfoot {
  font-weight: bold;
}
td > div.verse {
  white-space: pre;
}
p.table {
  margin-top: 0;
}
/* Because the table frame attribute is overriden by CSS in most browsers. */
div.tableblock > table[frame="void"] {
  border-style: none;
}
div.tableblock > table[frame="hsides"] {
  border-left-style: none;
  border-right-style: none;
}
div.tableblock > table[frame="vsides"] {
  border-top-style: none;
  border-bottom-style: none;
}


div.hdlist {
  margin-top: 0.8em;
  margin-bottom: 0.8em;
}
div.hdlist tr {
  padding-bottom: 15px;
}
dt.hdlist1.strong, td.hdlist1.strong {
  font-weight: bold;
}
td.hdlist1 {
  vertical-align: top;
  font-style: normal;
  padding-right: 0.8em;
  color: navy;
}
td.hdlist2 {
  vertical-align: top;
}
div.hdlist.compact tr {
  margin: 0;
  padding-bottom: 0;
}

.comment {
  background: yellow;
}

@media print {
  div#footer-badges { display: none; }
}

div#toctitle {
  color: #527bbd;
  font-family: sans-serif;
  font-size: 1.1em;
  font-weight: bold;
  margin-top: 1.0em;
  margin-bottom: 0.1em;
}

div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
  margin-top: 0;
  margin-bottom: 0;
}
div.toclevel2 {
  margin-left: 2em;
  font-size: 0.9em;
}
div.toclevel3 {
  margin-left: 4em;
  font-size: 0.9em;
}
div.toclevel4 {
  margin-left: 6em;
  font-size: 0.9em;
}
/* Workarounds for IE6's broken and incomplete CSS2. */

div.sidebar-content {
  background: #ffffee;
  border: 1px solid silver;
  padding: 0.5em;
}
div.sidebar-title, div.image-title {
  color: #527bbd;
  font-family: sans-serif;
  font-weight: bold;
  margin-top: 0.0em;
  margin-bottom: 0.5em;
}

div.listingblock div.content {
  border: 1px solid silver;
  background: #f4f4f4;
  padding: 0.5em;
}

div.quoteblock-attribution {
  padding-top: 0.5em;
  text-align: right;
}

div.verseblock-content {
  white-space: pre;
}
div.verseblock-attribution {
  padding-top: 0.75em;
  text-align: left;
}

div.exampleblock-content {
  border-left: 2px solid silver;
  padding-left: 0.5em;
}

/* IE6 sets dynamically generated links as visited. */
div#toc a:visited { color: blue; }
</style>
</head>
<body>
<div id="header">
<h1>store-it Documentation</h1>
<span id="author">Sylvain Fourmanoit</span><br />
<span id="email"><tt>&lt;<a href="mailto:syfou@users.sourceforge.net">syfou@users.sourceforge.net</a>&gt;</tt></span><br />
</div>
<div id="preamble">
<div class="sectionbody">
<div class="sidebarblock">
<div class="sidebar-content">
<div class="paragraph"><p>2009-10-12&#8201;&#8212;&#8201;the latest version of this document is always available
<a href="http://store-it.googlecode.com/svn/trunk/README.html">here</a>.
Back to <em>store-it</em> <a href="http://code.google.com/p/store-it/">page</a>.</p></div>
</div></div>
</div>
</div>
<h2 id="_what_is_store_it">What is store-it?</h2>
<div class="sectionbody">
<div class="paragraph"><p>Ever wanted to have access to a <em>content delivery network</em>
(<a href="http://en.wikipedia.org/wiki/Content_Delivery_Network">CDN</a>) to distribute some
of your static web stuff but the steep cost of the commercial offers
(<a href="http://www.akamai.com/">Akamai</a>, <a href="http://aws.amazon.com/cloudfront/">Amazon
CloudFront</a>, <a href="http://www.edgecast.com/">EdgeCast</a>, etc.) always stopped you?</p></div>
<div class="paragraph"><p>Thanks to the <em>Google App Engine</em> (<a href="http://code.google.com/appengine/">GAE</a>), now
you can, often for free, using <em>store-it</em>.</p></div>
<h3 id="_features">Features</h3><div style="clear:left"></div>
<div class="ulist"><ul>
<li>
<p>
Fast and reliable HTTP distribution of static resources, including support for
  partial requests and conditional caching
</p>
</li>
<li>
<p>
Filesystem-like, hierarchical storage
</p>
</li>
<li>
<p>
Authentication support (single user)
</p>
</li>
<li>
<p>
Redirection to default manual indexes
</p>
</li>
<li>
<p>
Automated (XHTML 1.1 compliant) indexes generation
</p>
</li>
<li>
<p>
Full resources listing, complete with dates and checksums, useful for
  site-wide syncing while minimizing data transfer
</p>
</li>
<li>
<p>
Pure HTTP resources updates (see the <a href="#usage">Usage Section</a> below for details)
</p>
</li>
</ul></div>
<h3 id="_capabilities">Capabilities</h3><div style="clear:left"></div>
<div class="paragraph"><p>Specifically, it allows, <strong>at absolutely no cost</strong>, to:</p></div>
<div class="ulist"><ul>
<li>
<p>
store up to one gigabyte of static content (in up to one megabyte chunks)
  right on Google infrastructure.
</p>
</li>
<li>
<p>
get the matching capability to renew (by upload) the full available space once
  a day.
</p>
</li>
<li>
<p>
serve up to one gigabyte of <em>http</em> or <em>https</em> traffic per day (or 56
  megabytes per minute, whichever comes first),
  <a href="http://code.google.com/status/appengine">reliably</a>.
</p>
</li>
</ul></div>
<div class="paragraph"><p>Of course, you can always get more resources at competitive prices if your
needs exceed this (see GAE
<a href="http://code.google.com/appengine/docs/quotas.html">quotas page</a>).</p></div>
<h3 id="_limitations">Limitations</h3><div style="clear:left"></div>
<div class="ulist"><ul>
<li>
<p>
Each individual request has to be at most one megabyte.
</p>
</li>
<li>
<p>
Unlike what happens with most commercial CDNs, you don&#8217;t have control over
  were your content will be delivered from; you will have to rely on Google&#8217;s
  black magic (which is pretty good anyway) for data duplication and such.
</p>
</li>
</ul></div>
</div>
<h2 id="_installation">Installation</h2>
<div class="sectionbody">
<div class="paragraph"><p><em>store-it</em> comes as a single file GAE application using the Python API. To
install, you just need to:</p></div>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
<a href="https://appengine.google.com/">Sign up</a> for a GAE account (it&#8217;s free).
</p>
</li>
<li>
<p>
<a href="http://code.google.com/appengine/downloads.html">Download</a> and install the <em>GAE
  SDK for Python</em>.
</p>
</li>
<li>
<p>
Fetch the <a href="http://store-it.googlecode.com/svn/trunk/store-it.py">latest version</a>
  of the <em>store-it.py</em> script and place it in your application.
</p>
</li>
<li>
<p>
Choose the base path under which you want to serve; it can either be the root
  of your application ( <em>/</em> ), or any other sub-path. Then, update your <em>app.yaml</em>
  file accordingly. It can be as simple as:
</p>
<div class="listingblock">
<div class="title">app.yaml example</div>
<div class="content">
<pre><tt>application: my-demo-app
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: store-it.py</tt></pre>
</div></div>
</li>
<li>
<p>
Finally, edit <em>store-it.py</em> global variables (near the top) to fit your
needs. Don&#8217;t forget to adapt the base path so it matches <em>app.yaml</em>.
</p>
</li>
</ol></div>
<div class="paragraph"><p>That&#8217;s it: you can now upload your updated application following your SDK
instructions, that will already be ready to serve.</p></div>
</div>
<h2 id="usage">Usage</h2>
<div class="sectionbody">
<div class="paragraph"><p><em>store-it</em> interface is basically just the good old <em>Hypertext Transfert
Protocol</em> (<a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">HTTP</a>), used in a
way that is probably pretty close to <a href="http://www.w3.org/People/Berners-Lee/">Tim
Berners-Lee</a> original idea. See it as a single-user, incomplete version of
WebDAV.</p></div>
<div class="paragraph"><p>Any user agent able to perform HTTP GET, PUT and DELETE requests as well as
dealing with cookies can be used to interface the application. Here is a pretty
complete <a href="http://store-it.googlecode.com/svn/trunk/store-it.sh">demo shell
script</a>, working on a GNU system, showing how it can be done.</p></div>
<h3 id="_basic_operations">Basic Operations</h3><div style="clear:left"></div>
<div class="dlist"><dl>
<dt class="hdlist1">
Access a resource
</dt>
<dd>
<p>
   Just perform a <tt>HTTP GET</tt> request on it (partial requests supported).
</p>
</dd>
<dt class="hdlist1">
Upload/update a resource
</dt>
<dd>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Get an authorization cookie by performing a <tt>HTTP GET</tt> on the secret URI
  (see below).
</p>
</li>
<li>
<p>
Perform a <tt>HTTP PUT</tt>, sending the authentication cookie along, and the
  payload as content.
</p>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">All paths below the base application directory are allowed, and are formed
following the usual web conventions. Be aware that you never need to care about
directories, as they are automatically created as needed. For instance, if you
decide to put something under <tt>a/b/c.png</tt>, directories <tt>a</tt> and <tt>b</tt> will be
made available as needed without further transactions.</td>
</tr></table>
</div>
</li>
</ol></div>
</dd>
<dt class="hdlist1">
Erase a resource
</dt>
<dd>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Get the authorization cookie (see below)
</p>
</li>
<li>
<p>
Perform a <tt>HTTP DELETE</tt> request on the resource URI.
</p>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">You can delete directories as well, as their content will be cleared. Performing
a delete on the base application directory will result in a full content erase.</td>
</tr></table>
</div>
</li>
</ol></div>
</dd>
</dl></div>
<h3 id="_special_operations">Special Operations</h3><div style="clear:left"></div>
<div class="dlist"><dl>
<dt class="hdlist1">
Administer the app
</dt>
<dd>
<p>
  Under <em>base_path/admin</em> (by default), you can get or set the authorization
  cookie path via an HTML form (only if you have administrative rights over the
  app, of course!). Default value is determined pseudo-randomly.
</p>
</dd>
<dt class="hdlist1">
Get the authorization cookie
</dt>
<dd>
<p>
  The cookie to reuse to perform all restricted operations can be obtained by
  performing a <tt>HTTP GET</tt> to the secret authorization path you can get while
  administering the app.
</p>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">Typically, the cookie will have a short life-span (120 seconds by default). This
means a user agent will usually ask for it right before performing any restricted
operation.</td>
</tr></table>
</div>
</dd>
<dt class="hdlist1">
Get a full resources listing
</dt>
<dd>
<div class="olist arabic"><ol class="arabic">
<li>
<p>
Get the authorization cookie (see above)
</p>
</li>
<li>
<p>
Get <em>base_path/list</em> (by default). You will receive a full list of available
    resources, complete with creation dates and <em>SHA1</em> checksums.
</p>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">Such a listing exists to allow fast bulk update of local resources to the app
data-store while minimizing bandwidth usage.</td>
</tr></table>
</div>
</li>
</ol></div>
</dd>
</dl></div>
</div>
<h2 id="_alternatives">Alternatives</h2>
<div class="sectionbody">
<div class="paragraph"><p>There are other codes and methods available to use GAE as a CDN:
<a href="http://www.google.com/search?q=GAE+CDN">look them up</a>. To name a few, look at:</p></div>
<div class="ulist"><ul>
<li>
<p>
<a href="http://code.google.com/p/gaedav/">gaedav</a>, a WebDAV interface to GAE (very
  interesting, but the author could not make it works reliably, and no
  authentication scheme is implemented)
</p>
</li>
<li>
<p>
<a href="http://cdn-cloud.appspot.com/">cnd-cloud</a>, CDN on GAE with HTML form upload
</p>
</li>
</ul></div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2009-10-12 16:27:03 EDT
</div>
</div>
</body>
</html>
